
#include "SysLib.h"
#include "SocketUtil.h"
#include "Common.h"


PSocketWrapper socket_new() {
    PSocketWrapper ret = NULL;
    zmalloc(&ret, sizeof(SocketWrapper));
#ifdef OS_WIN
    zmalloc(&ret->olRead, sizeof(OVERLAPPED));
    zmalloc(&ret->olWrite, sizeof(OVERLAPPED));
    ret->olRead->hEvent = CreateEvent(NULL,TRUE, FALSE, NULL);
    ret->olWrite->hEvent = CreateEvent(NULL,TRUE, FALSE, NULL);
    ret->reading = false;
    ret->writing = false;
#endif
    return ret;
}

void socket_delete(PSocketWrapper obj) {
    log_debug("Deleting socket");
#ifdef OS_WIN
    free(obj->olRead);
    free(obj->olWrite);
#endif
    free(obj);
}

int socket_write(PSocketWrapper sock, PBuffer buf) {
//    int send_size = min(buffer_readable(buf), 2880);
    int send_size = buffer_readable(buf);
    int ret = (int)send(sock->fd, buffer_pread(buf), send_size, 0);
    if (ret > 0) {
        buffer_pread_inc(buf, ret);
        buffer_compact(buf);
    }
    return ret;
}

int socket_read(PSocketWrapper sock, PBuffer buf) {
    int ret = (int)recv(sock->fd, buffer_pwrite(buf), buffer_writable(buf), 0);
    if (ret > 0) {
        buffer_pwrite_inc(buf, ret);
    }
    return ret;
}


PSocketWrapper socket_connect(const char *host, int port, bool blocking) {
    struct hostent *h = gethostbyname(host);
    if (h == NULL) {
        log_error("Error resolve host %s", host);
        return NULL;
    }

    PSocketWrapper ret = socket_new();

    ret->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ret->fd == INVALID_SOCKET) {
        log_error("socket() return error");
        socket_delete(ret);
        return NULL;
    }

    int flag = 1;
    if (-1 == setsockopt(ret->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag))) {
        log_error("Error setting tcp no delay");
        socket_close(ret);
        socket_delete(ret);
        return NULL;
    }

    if ((!blocking) && fcntl(ret->fd, F_SETFL, fcntl(ret->fd, F_GETFL) | O_NONBLOCK) < 0) {
        log_error("Error setting the socket in nonblocking mode");
        socket_close(ret);
        socket_delete(ret);
        return NULL;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = *(unsigned long * ) h->h_addr;
//    addr.sin_addr.s_addr = inet_addr(host);
    addr.sin_port = htons(port);

    
    if (SOCKET_ERROR == connect(ret->fd, (struct sockaddr *) &addr, sizeof(addr))) {
        if (errno != EINPROGRESS) {
            log_error("connect() return error");
            socket_close(ret);
            socket_delete(ret);
            return NULL;
        }
    }
    return ret;
}

void socket_close(PSocketWrapper sock) {
    log_debug("Closing socket");
#ifdef OS_WIN
    if (closesocket(sock->fd)) {
        log_error("Error closing socket, error: %d", WSAGetLastError());
    }
    CloseHandle(sock->olRead->hEvent);
    CloseHandle(sock->olWrite->hEvent);
#else
    if (close(sock->fd)) {
        log_error("Error closing socket, error: %d", errno);
    }
#endif
}
